zeebo/errs
GitHub: zeebo/errs
errs 是一个 Go 语言的错误处理库,通过 Class 分组、按需 stack trace、多层 Wrap 和 Group 收集等机制,让错误的创建、分类与诊断更加友好高效。
Stars: 57 | Forks: 7
# errs
[](https://godoc.org/github.com/zeebo/errs)
[](https://sourcegraph.com/github.com/zeebo/errs?badge)
[](https://goreportcard.com/report/github.com/zeebo/errs)
errs 是一个用于让错误变得更友好、更易于处理的 package。
### 创建错误
最简单的使用方式是使用 package 级的 [New][New] 函数。
它非常类似于 `fmt.Errorf`,但更好用。例如:
```
func checkThing() error {
return errs.New("what's up with %q?", "zeebo")
}
```
为什么说它更好?因为它自带的 stack trace 仅在格式化字符串中使用了 `"+"` 字符时才会打印。
这样既能保留诊断错误发生位置和原因的优势,又避免了在所有情况下都打印 stack trace 所产生的冗余信息。例如:
```
func doSomeRealWork() {
err := checkThing()
if err != nil {
fmt.Printf("%+v\n", err) // contains stack trace if it's a errs error.
fmt.Printf("%v\n", err) // does not contain a stack trace
return
}
}
```
### 错误 Class
你可以创建一个错误的 [Class][Class],并检查是否有任何错误是由该 class 创建的。
该 class 的名称会作为它所创建的所有错误的前缀。例如:
```
var Unauthorized = errs.Class("unauthorized")
func checkUser(username, password string) error {
if username != "zeebo" {
return Unauthorized.New("who is %q?", username)
}
if password != "hunter2" {
return Unauthorized.New("that's not a good password, jerkmo!")
}
return nil
}
func handleRequest() {
if err := checkUser("zeebo", "hunter3"); Unauthorized.Has(err) {
fmt.Println(err)
}
// output:
// unauthorized: that's not a good password, jerkmo!
}
```
Class 还可以 [Wrap][ClassWrap] 其他错误,并且错误可以被多次 wrap。例如:
```
var (
Error = errs.Class("mypackage")
Unauthorized = errs.Class("unauthorized")
)
func deep3() error {
return fmt.Errorf("ouch")
}
func deep2() error {
return Unauthorized.Wrap(deep3())
}
func deep1() error {
return Error.Wrap(deep2())
}
func deep() {
fmt.Println(deep1())
// output:
// mypackage: unauthorized: ouch
}
```
在上面的例子中,`Error.Has(deep1())` 和 `Unauthorized.Has(deep1())` 都会返回 `true`,并且 stack trace 只会在 `deep2` 调用时被记录一次。
此外,当一个错误已经被 wrap 后,再次使用相同的 class 去 wrap 它将不会产生任何效果。例如:
```
func doubleWrap() {
fmt.Println(Error.Wrap(Error.New("foo")))
// output:
// mypackage: foo
}
```
这样做是为了让你在决定是否应该 wrap 时更加轻松(你应该去 wrap)。
### 实用工具
[Classes][Classes] 是一个辅助函数,用于获取一个错误所包含的 class 的 slice。在这个 slice 中,最近一次 wrap 的排在最前面。例如:
```
func getClasses() {
classes := errs.Classes(deep1())
fmt.Println(classes[0] == &Error)
fmt.Println(classes[1] == &Unauthorized)
// output:
// true
// true
}
```
最后,提供了一个名为 [Unwrap][Unwrap] 的辅助函数,用于在你可能想要检查详细信息时获取被 wrap 的错误。例如:
```
var Error = errs.Class("mypackage")
func getHandle() (*os.File, error) {
fh, err := os.Open("neat_things")
if err != nil {
return nil, Error.Wrap(err)
}
return fh, nil
}
func checkForNeatThings() {
fh, err := getHandle()
if os.IsNotExist(errs.Unwrap(err)) {
panic("no neat things?!")
}
if err != nil {
panic("phew, at least there are neat things, even if i can't see them")
}
fh.Close()
}
```
它支持社区中常用的 `Unwrap() error` 和 `Unwrap() []error` 方法,并且会尽可能多地调用它们。
### Defer
该 package 还提供了 [Wrap][Wrap] 的 [WrapP][WrapP] 版本,这在 defer 上下文中非常有用。例如:
```
func checkDefer() (err error) {
defer Error.WrapP(&err)
fh, err := os.Open("secret_stash")
if err != nil {
return nil, err
}
return fh.Close()
}
```
### Group
[Groups][Group] 允许你收集一组错误。例如:
```
func tonsOfErrors() error {
var group errs.Group
for _, work := range someWork {
group.Add(maybeErrors(work))
}
return group.Err()
}
```
需要注意的几点:
- [Add][GroupAdd] 方法仅在传入的错误为非 nil 时才会将其添加到 group 中。
- [Err][GroupErr] 方法仅在添加了非 nil 错误时才返回一个错误;此外,如果只添加了一个错误,它会直接返回该错误。因此,我们始终可以保证,如果你只调用了 `group.Add(err)`,那么 `group.Err() == err`。
返回的错误会以类似如下的方式进行格式化:
```
func groupFormat() {
var group errs.Group
group.Add(errs.New("first"))
group.Add(errs.New("second"))
err := group.Err()
fmt.Printf("%v\n", err)
fmt.Println()
fmt.Printf("%+v\n", err)
// output:
// first; second
//
// group:
// --- first
// ... stack trace
// --- second
// ... stack trace
}
```
### 贡献
errs 是基于 MIT 许可证发布的。如果你想做出贡献,请务必将你自己添加到 AUTHORS 列表中。
标签:EVTX分析, Go, Ruby工具, SOC Prime, 堆栈跟踪, 开发工具, 日志审计, 编程库, 错误处理