合并多个错误字符串

Gre*_*etr 18 error-handling go

我是golang的新手,我的应用程序需要在循环中返回多个错误,以后需要组合并作为单个错误字符串返回.我无法使用字符串函数来组合错误消息.在返回之前,可以使用哪些方法将这些错误组合成单个错误?

package main

import (
   "fmt"
   "strings"
)

func Servreturn() (err error) {

   err1 = fmt.Errorf("Something else occured")
   err2 = fmt.Errorf("Something else occured again")

   // concatenate both the error

   return err3

}
Run Code Online (Sandbox Code Playgroud)

Dav*_*rth 18

字符串函数不能处理错误,因为错误实际上是一个实现函数Error()字符串的接口.

您可以在err1.Error()和err2.Error()上使用字符串函数,但不能在"err1"引用本身上使用.

有些错误是结构,比如从数据库驱动程序获得的结构.

所以没有自然的方法在错误上使用字符串函数,因为它们实际上可能不是字符串.

至于组合两个错误:

很简单,再次使用fmt.Errorf.

fmt.Errorf("Combined error: %v %v", err1, err2)
Run Code Online (Sandbox Code Playgroud)

或者:

errors.New(err1.Error() + err2.Error())
Run Code Online (Sandbox Code Playgroud)


Ina*_*mus 13

从 Go 1.20 开始,我们将能够使用Errors.Join包装多个错误。

请参阅此提案了解更多详细信息。

第一个选项

您可以打印错误;当您这样做时,它们将被换行符分隔。

var (
    ErrIncorrectUsername = errors.New("incorrect username")
    ErrIncorrectPassword = errors.New("incorrect password")
)

func main() {
    err := validate("ruster", "4321")
    // You can print multi-line errors
    // Each will be separated by a newline character (\n).
    if err != nil {
        fmt.Println(err)
        // incorrect username
        // incorrect password
    }
}

func validate(username, password string) error {
    var errs []error

    // errors.Join the errors into a single error
    if username != "gopher" {
        errs = append(errs, ErrIncorrectUsername)
    }
    if password != "1234" {
        errs = append(errs, ErrIncorrectPassword)
    }

    // Join returns a single `error`.
    // Underlying, the error contains all the errors we add.
    return errors.Join(errs...)
}
Run Code Online (Sandbox Code Playgroud)

第二个选项

errors.Join返回一个error包含您添加的每个错误的错误。因此,您可以使用errors.Iserrors.As来检查单个错误以获得更细的粒度。

// ...
func main() {
    err := validate("ruster", "4321")
    // You can detect each one:
    if errors.Is(err, ErrIncorrectUsername) {
        // handle the error here
    }
    // Or detect the other one:
    if errors.Is(err, ErrIncorrectPassword) {
        // handle the error here
    }
}

func validate(username, password string) error {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

注意:这个简单的validate例子是为了传达这个想法。不要将错误串联起来,而是将错误视为一棵树。Join与其他 s 结合使用时,您可以做到这一点Join


在 Go Playground 上运行两者


ask*_*skb 10

你可以使用strings.Join()append()函数来实现这个切片.

例如:golang playgorund

package main

import (
    "fmt"
    "strings"
    "syscall"
)

func main() {

    // create a slice for the errors
    var errstrings []string 

    // first error
    err1 := fmt.Errorf("First error:server error")
    errstrings = append(errstrings, err1.Error())

    // do something 
    err2 := fmt.Errorf("Second error:%s", syscall.ENOPKG.Error())
    errstrings = append(errstrings, err2.Error())

    // do something else
    err3 := fmt.Errorf("Third error:%s", syscall.ENOTCONN.Error())
    errstrings = append(errstrings, err3.Error())

    // combine and print all the error
    fmt.Println(fmt.Errorf(strings.Join(errstrings, "\n")))


}
Run Code Online (Sandbox Code Playgroud)

这将输出一个字符串,您可以将其发送回客户端.

First error:server1 
Second error:Package not installed 
Third error:Socket is not connected
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!


Ben*_*ell 7

为了扩展@WillC在评论中提到的内容,可以将自己的error类型定义为error接口类型。任何实现Error() string函数的类型都将实现error接口。因此,您可以创建一个CollectionError汇总错误并返回级联错误字符串的。

type ErrorCollector []error

func (c *ErrorCollector) Collect(e error) { *c = append(*c, e) }

func (c *ErrorCollector) Error() (err string) {
    err = "Collected errors:\n"
    for i, e := range *c {
        err += fmt.Sprintf("\tError %d: %s\n", i, e.Error())
    }

    return err
}
Run Code Online (Sandbox Code Playgroud)

这提供了将给定值附加error到切片的收集功能。调用时,Error() string它会在切片上进行迭代并创建一个串联的错误字符串。

func main() {
    collector := new(ErrorCollector)

    for i := 0; i < 10; i++ {
         collector.Collect(errors.New(fmt.Sprintf("%d Error", i)))
    }

    fmt.Println(collector)
}
Run Code Online (Sandbox Code Playgroud)

有一个很棒的golang.org博客文章详细介绍了错误。该示例的完整示例可在The Go Playground中找到


Mar*_*nce 7

人们可能对https://github.com/hashicorp/go-multierror感兴趣,它将自己描述为“一个 Go (golang) 包,用于将错误列表表示为单个错误。”。

  • 它带有 MPL 许可证,要求您公开源代码 (4认同)

Fec*_*ore 7

Go 1.13的更新:

Go版本1.13开始,该语言的错误包现在直接支持错误包装

您可以在中使用%w动词来包装错误fmt.Errorf

err := errors.New("Original error")
err = fmt.Errorf("%w; Second error", err)
Run Code Online (Sandbox Code Playgroud)

使用Unwrap删除最后添加的错误,并返回剩余的内容:previousErrors := errors.Unwrap(err)

错误的游乐场示例。展开

另外两个功能是errors.iserrors.As提供检查和检索特定类型错误的方法。

错误和错误的操场示例


Dave Cheney的出色errors软件包(https://github.com/pkg/errors)包含Wrap用于此目的的函数:

package main

import "fmt"
import "github.com/pkg/errors"

func main() {
        err := errors.New("error")
        err = errors.Wrap(err, "open failed")
        err = errors.Wrap(err, "read config failed")

        fmt.Println(err) // "read config failed: open failed: error"
}
Run Code Online (Sandbox Code Playgroud)

这还允许其他功能,例如解包错误原因:

package main

import "fmt"
import "github.com/pkg/errors"

func main() {
    err := errors.New("original error")
    err = errors.Wrap(err, "now this")

    fmt.Println(errors.Cause(err)) // "original error"
}
Run Code Online (Sandbox Code Playgroud)

以及指定时输出堆栈跟踪的选项fmt.Printf("%+v\n", err)

您可以在他的博客上找到有关该软件包的其他信息:此处此处

  • 换行不适用于多个错误。它用于用更多上下文包装一个错误。 (15认同)
  • @Paulo:@dolmen 断言,像其他语言中的“InnerException”一样使用时,错误包装是最直观的。包装的错误不是(兄弟)错误列表,而是(父子)错误链。没有什么可以阻止你像列表一样使用它,但在我看来,这是违反直觉的。 (2认同)

dol*_*men 5

Uber为这个用例提供了一个multierr包:

return multierr.Combine(err1, err2)
Run Code Online (Sandbox Code Playgroud)