Original address: Go Errors Detailed
Error handling in Golang is very different from PHP and JAVA, there is no try...catch
statement to handle errors. Therefore, Golang Error handling is a more controversial point, how to better understanding and processing error message is worth further study.
Go built-in errors
Go error
is an interface type that contains a Error()
method that returns string
. Any type that implements this interface can be used as an error, and the Error method provides a description of the error:
// http://golang.org/pkg/builtin/#error
// error 接口的定义
type error interface {
Error() string
}
// http://golang.org/pkg/errors/error.go
// errors 构建 error 对象
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
error
is an interface type that contains a Error()
method that returns string
. As long as the implementation of this interface
can be used as an error, Error
this method provides a description of the error.
error create
There are two ways to create error
1. errors.New()
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
return &errorString{text}
}
Q: Why does errors.New()
return a pointer?
A: To avoid the ambiguity caused by the equivalent content of New, look at the following example to understand why:
func main() {
errOne := errors.New("test")
errTwo := errors.New("test")
if errOne == errTwo {
log.Printf("Equal \n")
} else {
log.Printf("notEqual \n")
}
}
output:
notEqual
If errorString
is used for comparison, when the project becomes larger and more complex, New()
, and the troubleshooting of the problem at that time will also be catastrophic.
Sometimes we need more specific information. That is, specific "context" information is required, indicating specific error values.
This uses the fmt.Errorf
function
2. fmt.Errorf()
fmtErr := fmt.Errorf("fmt.Errorf() err, http status is %d", 404)
fmt.Printf("fmtErr errType:%T,err: %v\n", fmtErr, fmtErr)
output:
fmtErr errType is *errors.errorString,err is fmt.Errorf() err, http status is 404
Why is the error type returned by fmtErr
*errors.errorString
, didn't we create it fmt.Errorf()
Let's take a look at the source code together:
// Errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// If the format specifier includes a %w verb with an error operand,
// the returned error will implement an Unwrap method returning the operand. It is
// invalid to include more than one %w verb or to supply it with an operand
// that does not implement the error interface. The %w verb is otherwise
// a synonym for %v.
func Errorf(format string, a ...interface{}) error {
p := newPrinter()
p.wrapErrs = true
p.doPrintf(format, a)
s := string(p.buf)
var err error
if p.wrappedErr == nil {
err = errors.New(s)
} else {
err = &wrapError{s, p.wrappedErr}
}
p.free()
return err
}
By source can be found, p.wrappedErr
to nil
when calls errors.New()
to create an error.
The question is, what is p.wrappedErr
Let's look at an example:
wErrOne := errors.New("this is one ")
wErrTwo := fmt.Errorf("this is two %w", wErrOne)
fmt.Printf("wErrOne type is %T err is %v \n", wErrOne, wErrOne)
fmt.Printf("wErrTwo type is %T err is %v \n", wErrTwo, wErrTwo)
output:
wErrOne type is *errors.errorString err is this is one
wErrTwo type is *fmt.wrapError err is this is two this is one
Did you find it? error
object returned by %w, the output type is *fmt.wrapError
%w
is a new error handling feature added in go 1.13.
Go Error Handling Practices
How to get more detailed error information, such as stack trace
, to help locate the cause of the error?
Some people say that layer by layer, but this will cause the logs to be scattered everywhere, which is difficult to maintain.
Some people say , use recover
capture panic
, but this will lead to the abuse of panic
panic
only used in truly abnormal situations, such as
- When the program starts, if a strongly dependent service fails,
panic
exits - When the program starts, if it is found that the configuration obviously does not meet the requirements, you can
panic
(defense programming) - At the program entry, for example, gin middleware needs to use
recovery
prevent thepanic
program from exiting
pkg/errors library
Here, we try to solve the above problem with a small package github.com/pkg/errors
Look at a case:
package main
import (
"github.com/pkg/errors"
"log"
"os"
)
func main() {
err := mid()
if err != nil {
// 返回 err 的根本原因
log.Printf("cause is %+v \n", errors.Cause(err))
// 返回 err 调用的堆栈信息
log.Printf("strace tt %+v \n", err)
}
}
func mid() (err error) {
return test()
}
func test() (err error) {
_, err = os.Open("test/test.txt")
if err != nil {
return errors.Wrap(err, "open error")
}
return nil
}
output:
2022/01/17 00:26:17 cause is open test.test: no such file or directory
2022/01/17 00:26:17 strace tt open test.test: no such file or directory
open error
main.test
/path/err/wrap_t/main.go:41
main.mid
/path/err/wrap_t/main.go:35
main.main
/path/err/wrap_t/main.go:13
runtime.main
/usr/local/Cellar/go/1.17.2/libexec/src/runtime/proc.go:255
runtime.goexit
/usr/local/Cellar/go/1.17.2/libexec/src/runtime/asm_amd64.s:1581
pkg/errors
The upper caller can use the errors.Cause(err) method to get the culprit of this error.
// Wrap returns an error annotating err with a stack trace
// at the point Wrap is called, and the supplied message.
// If err is nil, Wrap returns nil.
func Wrap(err error, message string) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: message,
}
return &withStack{
err,
callers(),
}
}
// Is 指出当前的错误链是否存在目标错误。
func Is(err, target error) bool
// As 检查当前错误链上是否存在目标类型。若存在则ok为true,e为类型转换后的结果。若不存在则ok为false,e为空值
func As(type E)(err error) (e E, ok bool)
refer to
https://go.googlesource.com/proposal/+/master/design/29934-error-values.md
https://github.com/golang/go/labels/LanguageChange
https://go.googlesource.com/proposal/+/master/design/go2draft-error-values-overview.md
https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。